﻿
Imports System.Net
Imports std

Public Module _storoom
    Public storoom As storoomTalk = Nothing

    Sub New()
        storoom = New storoomTalk()
    End Sub
End Module

Public Class storoomTalk
    Implements comparableStorageInterface(Of storoomTalk), IComparable

    Private Class configNaming
        Public Const host As String = "host"
        Public Const port As String = "port"
        Public Const retrytimes As String = "retrytimes"
        Public Const holdtime As String = "holdtime"
        Public Const livetime As String = "livetime"

        Public Class defaultValue
            Public Const retrytimes As UInt64 = talkTime.client2room.retrytimes
            Public Const holdtime As Int64 = talkTime.client2room.holdtime
            Public Const livetime As Int64 = talkTime.client2room.livetime
        End Class
    End Class

    Public holdTime As Int64 = talkTime.client2room.holdtime
    Public liveTime As Int64 = talkTime.client2room.livetime
    Public maxTalkCount As Int32 = storoomc.connectionCount.node

    Public Delegate Function getstoroomDelegate(ByRef host As String, _
                                                ByRef port As Int32, _
                                                ByRef retrytimes As UInt64) As Boolean
    Private talkpool As talkpoolDefinition = Nothing
    Private getstoroom2 As getstoroomDelegate = Nothing

    Public Sub New()
        initial(CType(Nothing, getstoroomDelegate))
    End Sub

    Public Sub New(ByVal getstoroomFunction As getstoroomDelegate)
        initial(getstoroomFunction)
    End Sub

    Public Sub New(ByVal host As String, ByVal port As Int32, _
                   Optional ByVal retrytimes As UInt64 = talkTime.client2room.retrytimes)
        initial(host, port, retrytimes)
    End Sub

    Public Sub initial(ByVal getstoroomFunction As getstoroomDelegate)
        If Not talkpool Is Nothing Then
            talkpool.close()
        End If
        talkpool = New talkpoolDefinition(maxTalkCount)
        getstoroom2 = getstoroomFunction
    End Sub

    Private Function getstoroom(ByRef host As String, _
                                ByRef port As Int32, _
                                ByRef retrytimes As UInt64) As Boolean
        assert(Not getstoroom2 Is Nothing, "should set getstoroom2 delegate before any storoom operations.")
        If getstoroom2(host, port, retrytimes) Then
            kickLeftRightBlank(host)
            If retrytimes = 0 Then
                retrytimes = common.talkTime.client2room.retrytimes
            End If
            Return Not isemptystring(host) AndAlso port > 0
        Else
            Return False
        End If
    End Function

    Public Sub initial(ByVal host As String, ByVal port As Int32, _
                       Optional ByVal retrytimes As UInt64 = talkTime.client2room.retrytimes)
        copy(Me.host, host)
        copy(Me.port, port)
        copy(Me.retrytimes, retrytimes)
        initial(AddressOf defaultGetStoroom)
    End Sub

    Public Sub initial(ByVal address As IPAddress, ByVal port As Int32, _
                       Optional ByVal retrytimes As UInt64 = talkTime.client2room.retrytimes)
        If Not address Is Nothing Then
            initial(Convert.ToString(address), port, retrytimes)
        End If
    End Sub

    Public Function initial(ByVal config As configParser, Optional ByVal section As String = "storoom") As Boolean
        If config Is Nothing OrElse isemptystring(section) Then
            Return False
        End If

        If Not config.getparameter(section, configNaming.host, host) Then
            Return False
        End If
        If Not config.getparameter(section, configNaming.port, port) Then
            Return False
        End If
        config.getparameter(section, configNaming.retrytimes, retrytimes, configNaming.defaultValue.retrytimes)
        config.getparameter(section, configNaming.holdtime, holdTime, configNaming.defaultValue.holdtime)
        config.getparameter(section, configNaming.livetime, liveTime, configNaming.defaultValue.livetime)
        initial(AddressOf defaultGetStoroom)
        Return True
    End Function

    Private host As String = Nothing
    Private port As Int32 = npos
    Private retrytimes As UInt64 = talkTime.client2room.retrytimes

    Private Function defaultGetStoroom(ByRef host As String, ByRef port As Int32, ByRef retrytimes As UInt64) As Boolean
        copy(host, Me.host)
        copy(port, Me.port)
        copy(retrytimes, Me.retrytimes)
        Return True
    End Function

    Private Delegate Function _do(Of T1, T2, T3) _
                                 (ByRef talk As tcpTalk, _
                                  ByRef i1 As T1, ByRef i2 As T2, ByRef i3 As T3, _
                                  ByVal host As String, ByVal port As Int32, ByVal retrytimes As UInt64, _
                                  ByVal holdtime As Int64, ByVal livetime As Int64) _
                                 As returnValue

    Private Delegate Function _do(Of T1, T2) _
                                 (ByRef talk As tcpTalk, _
                                  ByRef i1 As T1, ByRef i2 As T2, _
                                  ByVal host As String, ByVal port As Int32, ByVal retrytimes As UInt64, _
                                  ByVal holdtime As Int64, ByVal livetime As Int64) _
                                 As returnValue

    Private Delegate Function _do(Of T1) _
                                 (ByRef talk As tcpTalk, _
                                  ByRef i1 As T1, _
                                  ByVal host As String, ByVal port As Int32, ByVal retrytimes As UInt64, _
                                  ByVal holdtime As Int64, ByVal livetime As Int64) _
                                 As returnValue
    Private Delegate Function _do(ByRef talk As tcpTalk, _
                                  ByVal host As String, ByVal port As Int32, ByVal retrytimes As UInt64, _
                                  ByVal holdtime As Int64, ByVal livetime As Int64) _
                                 As returnValue

    Private Function _talk(ByVal _do As _do) As Boolean
        assert(Not _do Is Nothing)
        Dim _do2 As _do(Of Object, Object, Object) = _
            Function(ByRef _t As tcpTalk, _
                     ByRef _N1 As Object, ByRef _N2 As Object, ByRef _N3 As Object, _
                     host As String, port As Int32, retrytimes As UInt64, _
                     holdtime As Int64, livetime As Int64) As returnValue
                Return _do(_t, host, port, retrytimes, holdtime, livetime)
            End Function
        Return _talk(Nothing, Nothing, Nothing, _do2)
    End Function

    Private Function _talk(Of T1)(ByRef i1 As T1, _
                                  ByVal _do As _do(Of T1)) As Boolean
        assert(Not _do Is Nothing)
        Dim _do2 As _do(Of T1, Object, Object) = _
            Function(ByRef _t As tcpTalk, _
                     ByRef _i1 As T1, ByRef _N1 As Object, ByRef _N2 As Object, _
                     host As String, port As Int32, retrytimes As UInt64, _
                     holdtime As Int64, livetime As Int64) As returnValue
                Return _do(_t, _i1, host, port, retrytimes, holdtime, livetime)
            End Function
        Return _talk(i1, Nothing, Nothing, _do2)
    End Function

    Private Function _talk(Of T1, T2)(ByRef i1 As T1, ByRef i2 As T2, _
                                      ByVal _do As _do(Of T1, T2)) As Boolean
        assert(Not _do Is Nothing)
        Dim _do2 As _do(Of T1, T2, Object) = _
            Function(ByRef _t As tcpTalk, _
                     ByRef _i1 As T1, ByRef _i2 As T2, ByRef _N2 As Object, _
                     host As String, port As Int32, retrytimes As UInt64, _
                     holdtime As Int64, livetime As Int64) As returnValue
                Return _do(_t, _i1, _i2, host, port, retrytimes, holdtime, livetime)
            End Function
        Return _talk(i1, i2, Nothing, _do2)
    End Function

    Private Function _talk(Of T1, T2, T3)(ByRef i1 As T1, ByRef i2 As T2, ByRef i3 As T3, _
                                          ByVal _do As _do(Of T1, T2, T3)) As Boolean
        assert(Not _do Is Nothing)

        Dim talk As tcpTalk = Nothing
        Dim index As Int64

        If talkpool.getfree(index, talk) Then
            Dim host As String = Nothing
            Dim port As Int32 = 0
            Dim retrytimes As UInt64 = 0
            Dim rtn As Boolean
            If getstoroom(host, port, retrytimes) Then
                Dim rst As returnValue
                rst = _do(talk, i1, i2, i3, host, port, retrytimes, holdTime, liveTime)
                If rst = returnValue.success Then
                    rtn = True
                ElseIf rst = returnValue.fail Then
                    rtn = False
                ElseIf rst = returnValue.overtime Then
                    raiseError("cannot talk with storoom " + talk.display() + ", overtime, ignore request.", _
                               errorHandle.errorType.warning)
                    rtn = False
                Else
                    assert(False, "all returnValue should be covered.")
                End If
            Else
                raiseError("cannot get storoom host / port / retrytimes, ignore request.", _
                           errorHandle.errorType.warning)
                rtn = False
            End If

            talkpool.release(index)
            Return rtn
        Else
            raiseError("cannot get free tcpTalk instance from talkpool, ignore request.", _
                       errorHandle.errorType.warning)
            Return False
        End If
    End Function

    Public Function list(ByRef l As vector(Of String)) As Boolean Implements storageInterface.list
        Dim _do As _do(Of vector(Of String)) = _
            Function(ByRef _t As tcpTalk, ByRef _l As vector(Of String), _
                     host As String, port As Int32, retrytimes As UInt64, _
                     holdtime As Int64, livetime As Int64) As returnValue
                Return storclient.trylist(_t, host, port, _l, retrytimes, holdtime, livetime)
            End Function
        Return _talk(l, _do)
    End Function

    Public Function list(ByRef s As [set](Of String)) As Boolean
        Dim l As vector(Of String) = Nothing
        If list(l) Then
            If Not l Is Nothing Then
                assert(vector2set(l, s), "cannot convert vector to set.")
            End If
            Return True
        Else
            Return False
        End If
    End Function

    Public Function read(ByVal key As String, ByRef buff As bytes) As Boolean Implements storageInterface.read
        Dim _do As _do(Of bytes) = _
            Function(ByRef _t As tcpTalk, ByRef _b As bytes, _
                     host As String, port As Int32, retrytimes As UInt64, _
                     holdtime As Int64, livetime As Int64) As returnValue
                Return storclient.tryread(_t, host, port, key, _b, retrytimes, holdtime, livetime)
            End Function
        Return _talk(buff, _do)
    End Function

    Public Function write(ByVal key As String, ByVal buff As bytes) As Boolean Implements storageInterface.write
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.trywrite(_t, host, port, key, buff, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function uniqueWrite(ByVal key As String, ByVal buff As bytes) As Boolean _
                               Implements storageInterface.uniqueWrite
        Dim _do As _do = _
            Function(ByRef _t As tcpTalk, _
                     host As String, port As Int32, retrytimes As UInt64, _
                     holdtime As Int64, livetime As Int64) As returnValue
                Return storclient.tryuniqueWrite(_t, host, port, key, buff, retrytimes, holdtime, livetime)
            End Function
        Return _talk(_do)
    End Function

    Public Function modify(ByVal key As String, ByVal buff As bytes) As Boolean Implements storageInterface.modify
        Dim _do As _do = _
          Function(ByRef _t As tcpTalk, _
                   host As String, port As Int32, retrytimes As UInt64, _
                   holdtime As Int64, livetime As Int64) As returnValue
              Return storclient.trymodify(_t, host, port, key, buff, retrytimes, holdtime, livetime)
          End Function
        Return _talk(_do)
    End Function

    Public Function seek(ByVal key As String) As Boolean Implements storageInterface.seek
        Dim _do As _do = _
          Function(ByRef _t As tcpTalk, _
                   host As String, port As Int32, retrytimes As UInt64, _
                   holdtime As Int64, livetime As Int64) As returnValue
              Return storclient.tryseek(_t, host, port, key, retrytimes, holdtime, livetime)
          End Function
        Return _talk(_do)
    End Function

    Public Function delete(ByVal key As String) As Boolean Implements storageInterface.delete
        Dim _do As _do = _
          Function(ByRef _t As tcpTalk, _
                   host As String, port As Int32, retrytimes As UInt64, _
                   holdtime As Int64, livetime As Int64) As returnValue
              Return storclient.trydelete(_t, host, port, key, retrytimes, holdtime, livetime)
          End Function
        Return _talk(_do)
    End Function

    Public Function sizeof(ByVal key As String, ByRef size As UInt64) As Boolean Implements storageInterface.sizeof
        Dim _do As _do(Of UInt64) = _
        Function(ByRef _t As tcpTalk, ByRef _s As UInt64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.trysizeof(_t, host, port, key, _s, retrytimes, holdtime, livetime)
        End Function
        Return _talk(size, _do)
    End Function

    Public Function keycount(ByRef count As UInt64) As Boolean Implements storageInterface.keycount
        Dim _do As _do(Of UInt64) = _
        Function(ByRef _t As tcpTalk, ByRef _c As UInt64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.trykeycount(_t, host, port, _c, retrytimes, holdtime, livetime)
        End Function
        Return _talk(count, _do)
    End Function

    Public Function full() As Boolean Implements storageInterface.full
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryfull(_t, host, port, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function health() As Boolean Implements storageInterface.health
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            'more severe for health check
            Return storclient.tryhealth(_t, host, port, _
                                       ifoper(Of UInt64)((retrytimes >> 2) < talkTime.share.minHealthRetryTimes, _
                                                         talkTime.share.minHealthRetryTimes, retrytimes >> 2), _
                                       ifoper(Of Int64)((holdtime >> 5) < talkTime.share.minHealthHoldTime, _
                                                        talkTime.share.minHealthHoldTime, holdtime >> 5), livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function retire() As Boolean Implements storageInterface.retire
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryretire(_t, host, port, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function empty() As Boolean Implements storageInterface.empty
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryempty(_t, host, port, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function capacity(ByRef cap As UInt64) As Boolean Implements storageInterface.capacity
        Dim _do As _do(Of UInt64) = _
        Function(ByRef _t As tcpTalk, ByRef _c As UInt64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.trycapacity(_t, host, port, _c, retrytimes, holdtime, livetime)
        End Function
        Return _talk(cap, _do)
    End Function

    Public Function valuesize(ByRef size As UInt64) As Boolean Implements storageInterface.valuesize
        Dim _do As _do(Of UInt64) = _
        Function(ByRef _t As tcpTalk, ByRef _s As UInt64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryvaluesize(_t, host, port, _s, retrytimes, holdtime, livetime)
        End Function
        Return _talk(size, _do)
    End Function

    Public Function status(ByRef allslicesCount As UInt64, _
                           ByRef unhealthSlicesCount As UInt64) As Boolean Implements storageInterface.status
        Dim _do As _do(Of UInt64, UInt64) = _
        Function(ByRef _t As tcpTalk, ByRef _a As UInt64, ByRef _u As UInt64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.trystatus(_t, host, port, _a, _u, retrytimes, holdtime, livetime)
        End Function
        Return _talk(allslicesCount, unhealthSlicesCount, _do)
    End Function

    'wait all the tcp connections close instead of close the stornode
    Public Function close() As Boolean Implements storageInterface.close
        talkpool.close()
        Return True
    End Function

    'do not need to send open command to storoom & stornode
    Public Function open() As Boolean Implements storageInterface.open
        Return True
    End Function

    Public Function sync(ByVal desthost As String, ByVal destport As Int32) As Boolean Implements storageInterface.sync
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.trysync(_t, host, port, desthost, destport, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    'help function, so do no need to worry about the return
    Public Function version() As String
        Dim rtn As String = Nothing
        Dim _do As _do(Of String) = _
        Function(ByRef _t As tcpTalk, ByRef _v As String, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryversion(_t, host, port, _v, retrytimes, holdtime, livetime)
        End Function
        If _talk(rtn, _do) Then
            Return rtn
        Else
            Return Nothing
        End If
    End Function

    Public Function tsRead(ByVal key As String, ByRef buff As bytes, ByRef ts As Int64) As Boolean
        Dim _do As _do(Of bytes, Int64) = _
        Function(ByRef _t As tcpTalk, ByRef _b As bytes, ByRef _ts As Int64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsRead(_t, host, port, key, _b, _ts, retrytimes, holdtime, livetime)
        End Function
        Return _talk(buff, ts, _do)
    End Function

    Public Function tsWrite(ByVal key As String, ByVal buff As bytes, ByVal ts As Int64) As Boolean
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsWrite(_t, host, port, key, buff, ts, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function tsModify(ByVal key As String, ByVal buff As bytes, ByVal ts As Int64) As Boolean
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsModify(_t, host, port, key, buff, ts, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function tsSeek(ByVal key As String) As Boolean
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsSeek(_t, host, port, key, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function tsList(ByRef l As vector(Of String)) As Boolean
        Dim _do As _do(Of vector(Of String)) = _
        Function(ByRef _t As tcpTalk, ByRef _l As vector(Of String), _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsList(_t, host, port, _l, retrytimes, holdtime, livetime)
        End Function
        Return _talk(l, _do)
    End Function

    Public Function tsDelete(ByVal key As String) As Boolean
        Dim _do As _do = _
        Function(ByRef _t As tcpTalk, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsDelete(_t, host, port, key, retrytimes, holdtime, livetime)
        End Function
        Return _talk(_do)
    End Function

    Public Function tsSizeof(ByVal key As String, ByRef size As UInt64) As Boolean
        Dim _do As _do(Of UInt64) = _
        Function(ByRef _t As tcpTalk, ByRef _s As UInt64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsSizeof(_t, host, port, key, _s, retrytimes, holdtime, livetime)
        End Function
        Return _talk(size, _do)
    End Function

    Public Function tsKeyCount(ByRef count As UInt64) As Boolean
        Dim _do As _do(Of UInt64) = _
        Function(ByRef _t As tcpTalk, ByRef _c As UInt64, _
                 host As String, port As Int32, retrytimes As UInt64, _
                 holdtime As Int64, livetime As Int64) As returnValue
            Return storclient.tryTsKeyCount(_t, host, port, _c, retrytimes, holdtime, livetime)
        End Function
        Return _talk(count, _do)
    End Function

    'this CompareTo will out-of-order if getstoroom returns different results in each call
    'storoom/stornode.vb returns always the same storoom, so it's safe to use it in storoom
    Public Function CompareTo(ByVal obj As Object) As Integer Implements IComparable.CompareTo
        Return cast_compare(Of storoomTalk, Object, storoomTalk)(Me, obj)
    End Function

    Public Function CompareTo(ByVal that As storoomTalk) As Integer Implements IComparable(Of storoomTalk).CompareTo
        If Me Is Nothing AndAlso that Is Nothing Then
            Return 0
        ElseIf Me Is Nothing Then
            Return -1
        ElseIf that Is Nothing Then
            Return 1
        Else
            Dim thisHost As String = Nothing
            Dim thisPort As Int32
            Dim thatHost As String = Nothing
            Dim thatPort As Int32
            debugassert(getstoroom(thisHost, thisPort, Nothing), "should have a valid getstoroom return.")
            debugassert(that.getstoroom(thatHost, thatPort, Nothing), "should have a valid getstoroom return.")

            Dim cmp As Int32
            cmp = strcmp(thisHost, thatHost, False)
            If cmp = 0 Then
                Return thisPort - thatPort
            Else
                Return cmp
            End If
        End If
    End Function
End Class

Public Module _storoomTalks
    Public storoomTalks As storoomTalkPool = Nothing

    Sub New()
        storoomTalks = New storoomTalkPool()
    End Sub
End Module

Public Class storoomTalkPool
    Public Const defaultSize As Int64 = 128
    Public retrytimes As Int64 = 4
    'storoomTalk is not cloneable, do not need to use pointer here
    Private p As cacheManager(Of IPEndPoint2, storoomTalk) = Nothing

    Public Sub New(ByVal size As Int64)
        p = New cacheManager(Of IPEndPoint2, storoomTalk) _
                            (cachesize:=size, threadsafe:=False, retireticks:=npos)
    End Sub

    Public Sub New()
        Me.New(defaultSize)
    End Sub

    Default Public ReadOnly Property at(ByVal address As IPEndPoint2) As storoomTalk
        Get
            If Not address Is Nothing Then
                Dim pnt As storoomTalk = Nothing
                SyncLock p
                    If Not p.get(address, pnt) Then
                        pnt = New storoomTalk(Convert.ToString(address.Address), address.Port, retrytimes)
                        p.add(address, pnt)
                    End If
                End SyncLock

                Return pnt
            Else
                Return Nothing
            End If
        End Get
    End Property

    Public Function [get](ByVal host As String, ByVal port As Int32) As storoomTalk
        Return at(IPEndPoint2.create(host, port))
    End Function
End Class
